package esercitazione5.graphs;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * Classe generica  per la gestione di grafi orientati
 *
 * @param <V>
 * @param <E>
 */
public class Graph<V,E> implements GraphInterface<V,E>{
	
	// Collezione di vertici (key) e la relativa lista di vertici uscenti (value)
	Map<Vertex<V>,LinkedList<Edge<V>>> g;
	
	/**
	 * Costruttore di default che inizializza la struttura alla base del grafo
	 */
	public Graph() {
		g = new HashMap<Vertex<V>, LinkedList<Edge<V>>>();
	}

	/**
	 * Ritorna una collezione iterabile di tutti i vertici
	 */
	@Override
	public Iterable<Vertex<V>> vertices() {
		return g.keySet();
	}

	/**
	 *  Metodo per ottenere una collezione iterabile di archi orientati monodirezionali
	 */
	@Override
	public Iterable<Edge<V>> edges() {
		LinkedList<Edge<V>> l = new LinkedList<Edge<V>>();
		for(LinkedList<Edge<V>> n : g.values())
			l.addAll(n);
		return l;
	}

	/**
	 * Metodo per inserire un vertice nel grafo, senza alcun collegamento.
	 */
	@Override
	public Vertex<V> insertVertex(V e) {
		Vertex<V> v = new GraphVertex<V>(e);
		g.put(v, new LinkedList<Edge<V>>());
		return v;
	}

	/**
	 * Metodo per inserire un edge nel grafo, dati i vertici sorgenti e destinazione e il peso dell'arco.
	 * @param s Vertice sorgente
	 * @param d Vertice destinazione
	 * @param e Peso dell'arco
	 */
	@Override
	public Edge<V> insertEdge(Vertex<V> s, Vertex<V> d, E e) {
		Edge<V> edge = new GraphEdge<V>(s, d);
		edge.addProperty("weight", e);
		g.get(s).add(edge);
		return edge;
	}

	/**
	 * Metodo per ottenere una collezione iterabile di archi orientati monodirezionali relativi ad un singolo vertice.
	 */
	@Override
	public Iterable<Edge<V>> outgoingEdges(Vertex<V> v) {
		return g.get(v);
	}

	/**
	 * Metodo per ottenere il numero di archi del grafo.
	 */
	@Override
	public int numEdges() {
		int i = 0;
		for(LinkedList<Edge<V>> n : g.values())
			i+=n.size();
		return i;
	}

	/**
	 * Metodo per ottenere il numero di vertici del grafo.
	 */
	@Override
	public int numVertices() {
		return g.size();
	}

}
